<?php
/** @name : Database.php via KOOK-FRAMEWORK - @author : Theerasak Phuetthanyakij - @since : 25 �.�. 2555 14:46:40 - @encoding : UTF-8 */

class DB
{
	public static $connection;
	private $statement,$sql,$result,$data,$resultRows;
	private static $versionText="VERSION";

	private function getStatement()
	{
		return $this->statement;
	}

	private function setStatement($statement)
	{
		$this->statement = $statement;
	}

	private function getSql()
	{
		return $this->sql;
	}

	private function setSql($sql)
	{
		$this->sql = $sql;
	}

	private function getResult()
	{
		return $this->result;
	}

	private function setResult($result)
	{
		$this->result = $result;
	}

	private function getData()
	{
		return $this->data;
	}

	private function setData($data)
	{
		$this->data = $data;
	}

	private function getResultRows()
	{
		return $this->resultRows;
	}

	private function setResultRows($resultRows)
	{
		$this->resultRows = $resultRows;
	}

	/**
	 * @method สร้าง Connection เพื่อติดต่อกับฐานข้อมูล
	 * @param ไม่มี
	 * @return ไม่มี
	 * @example DB::connect();
	 */
	public static function connect()
	{
		try
		{
			// เชื่อมต่อกับฐานข้อมูลและเลือกฐานข้อมูล
			@self::$connection = mysql_connect(Config::$dbHost, Config::$dbUser, Config::$dbPass);
			$dbselect = mysql_select_db(Config::$dbName,self::$connection);

			// ตรวจสอบหากมีการตั้งค่า Charset ในไฟล์ Config ให้ตั้งค่าให้กับ Database ด้วย
			if(Config::$dbCharset!=""){
				self::setCharset(Config::$dbCharset);
			}

			if (!self::$connection)
			{
				throw new DatabaseException("ไม่สามารถเชื่อมต่อกับฐานข้อมูลได้กรุณาไปตั้งค่าได้ที่ <a href=\"".Config::site()."/install\" target=\"_blank\">".Config::site()."/install</a><br/>");
			}
			else if(!$dbselect)
			{
				throw new DatabaseException("เชื่อมต่อฐานข้อมูลสำเร็จ แต่ชื่อของฐานข้อมูล (DBNAME) ไม่ถูกต้อง กรุณาไปตั้งค่าได้ที่ <a href=\"".Config::site()."/install\" target=\"_blank\">".Config::site()."/install</a><br/>");
			}
			else
			{
				// เชื่อมต่อกับฐานข้อมูลสำเร็จสมบูรณ์แล้ว
				Console::write("SQL: CONNECTION ESTABLISHED");
			}
		}
		catch (DatabaseException $e)
		{
			$e->execute();
		}
	}

	public static function setCharset($charset)
	{
		mysql_query("SET NAMES ".$charset);
		mysql_query("SET CHARACTER_SET ".$charset);
	}

	private function query()
	{
		try
		{
			if(Config::$dbConnect)
			{
				Console::write("SQL: ".$this->getSql());
				$result = mysql_query($this->getSql());
				if($result)
				{
					$this->setResult($result);
					$this->setResultRows(mysql_num_rows($result));
				}
				else
				{
					if(preg_match("/^[Table ]+[a-zA-Z0-9 '_.]+[doesn't exist]+$/", mysql_error(),$matches))
					{
						throw new DatabaseException(MSG_ERR_DATABASE_ERROR."<br/><br/><b>".MSG_SYSTEM_MESSAGE."</b><br/>:- ".mysql_error()."<br/><br/><b>".MSG_DATABASE_QUERY_STRING."</b><br/>:- ".$this->sql."<br/>");
					}
				}
			}
			else
			{
				throw new DatabaseException(MSG_ERR_DATABASE_ERROR."<br/><br/><b>".MSG_SYSTEM_MESSAGE."</b><br/>:- ระบบถูกตั้งค่าไม่ให้เชื่อมต่อกับฐานข้อมูล กรุณาแก้ไขการเชื่อมต่อได้ที่ <a href=\"".Config::site()."/install\" target=\"_blank\">".Config::site()."/install</a>");
			}
		}
		catch (DatabaseException $e)
		{
			$e->execute();
		}
	}

	private function createStatement()
	{
		return self::$connection->stmt_init();
	}

	private function clearStatement()
	{
		$this->getStatement().close();
	}

	/**
	 * @method บันทึกข้อมูล Instance สู่ฐานข้อมูล โดยระบบจะทราบเองว่าจะทำการ สร้างหรือปรับปรุงข้อมูล
	 * @param instance = Bean ที่ต้องการนำไปบันทึกเข้าสู่ฐานข้อมูล, versionIgnore = กรณีปรับปรุงข้อมูลหากไม่ต้องการสนใจเวอร์ชั่นของข้อมูลให้ตั้งเป็น TRUE
	 * @return สถานะว่าบันทึกข้อมูลสำเร็จหรือไม่
	 * @example $this->getDBConnection()->save($instance);
	 */
	public function save($instance,$versionIgnore=FALSE)
	{
		if($instance->getState()=="insert")
			return $this->insert($instance,$versionIgnore);
		else if($instance->getState()=="update")
			return $this->update($instance,$versionIgnore);
	}

	private function insert($instance,$versionIgnore=FALSE)
	{
		$database = $instance->getDB();
		try
		{
			$statement = array("","");

			foreach($database['columns'] as $data)
			{
				if($data['constraint']['auto'] == false)
				{
					self::validate($data);
					if($statement[0] != "" && $statement[1]!=""){
						$statement[0].=",";$statement[1].=",";
					}

					$statement[0] .= "`".$data['name']."`";

					if($data['constraint']['type']!="integer" && $data['constraint']['type']!="float" && $data['constraint']['type']!="double")
						$statement[1] .= "'".$data['value']."'";
					else
						$statement[1] .= $data['value'];

				}
			}


			$this->setSql("INSERT INTO `".$database['tableName']."`(".$statement[0].") VALUES(".$statement[1].")");
			$this->query();
			if($this->getResultRows()<=0 && mysql_error(self::$connection))
			{
				throw new DatabaseException(MSG_ERR_DATABASE_CANNOT_SAVE."<br/><br/><b>".MSG_SYSTEM_MESSAGE."</b><br/>:- ".mysql_error(self::$connection)."<br/><br/><b>".MSG_DATABASE_QUERY_STRING."</b><br/>:- ".$this->sql."<br/>");
			}
			else
			{
				return $instance;
			}
		}
		catch (DatabaseException $e)
		{
			$e->execute();
		}

	}

	/**
	 * @method ลบค่าข้อมูลออกจากฐานข้อมูลโดยดูจากเงื่อนไขจาก primary key ของ instance ที่ส่งมา
	 * @param instance = Bean ที่ต้องการนำไปเป็นเงื่อนไขในการลบ
	 * @return สถานะการลบว่าลบข้อมูลสำเร็จหรือไม่
	 * @example $this->getDBConnection()->delete($instance);
	 */
	public function delete($instance)
	{
		$database = $instance->getDB();
		try
		{
			$statement;

			foreach($database['columns'] as $data)
			{
				if($data['constraint']['primary'] == true)
				{
					self::validate($data);
					if($statement != ""){
						$statement.=" AND ";
					}

					$statement .= "`".$data['name']."` = ";

					if($data['constraint']['type']!="integer" && $data['constraint']['type']!="float" && $data['constraint']['type']!="double")
						$statement .= "'".$data['value']."'";
					else
						$statement .= $data['value'];

				}
			}

			$this->setSql("DELETE FROM `".$database['tableName']."` WHERE ".$statement);

			$this->query();

			if($this->getResultRows()<=0 && mysql_error(self::$connection))
			{
				throw new DatabaseException(MSG_ERR_DATABASE_CANNOT_SAVE."<br/><br/><b>".MSG_SYSTEM_MESSAGE."</b><br/>:- ".mysql_error(self::$connection)."<br/><br/><b>".MSG_DATABASE_QUERY_STRING."</b><br/>:- ".$this->sql."<br/>");
			}
			else
			{
				return TRUE;
			}
		}
		catch (DatabaseException $e)
		{
			$e->execute();
		}
	}

	/**
	 * @method วนเพื่อกรอกค่าเข้ากับ
	 * @param database = ฐานข้อมูลที่จะใช้ในการ mapping ข้อมูล
	 * @example setToObject($database);
	 */
	private function setToObject($database)
	{
		$instance = $database['modelName']."Manager";
		$instance = new $instance();

		foreach ($database['columns'] as $key => $column)
		{
			$data = mysql_result($this->getResult(),0,$database['tableName'].".".$column['name']);
			settype($data,$column['constraint']['type']);
			$instance->set($key,$data);
		}
		$instance->setState("update");
		$instance->setVersion(mysql_result($this->getResult(),0,$database['tableName'].'.'.self::$versionText));
		return $instance;
	}

	private function setMapValueToStatement($database)
	{
		$statement = "";
		foreach($database['columns'] as $data)
		{
			if($data['value'] != "")
			{
				//self::validate($data);
				$statement.=" AND ";

				$statement .= "`".$data['name']."` = ";

				//if($data['constraint']['type']!="integer" && $data['constraint']['type']!="float" && $data['constraint']['type']!="double")
				$statement .= "'".$data['value']."'";
				//else
				//$statement .= $data['value'];

			}
		}

		return $statement;
	}

	/**
	 * @method ค้นหาและดึงค่าข้อมูลจากฐานข้อมูลโดยจะดึงเพียงค่าเดียวเท่านั้น
	 * @param instance = Bean ที่ต้องการนำไปเป็นเงื่อนไขในการค้นหา
	 * @return Bean ข้อมูลที่ได้จากการค้นหา
	 * @example $this->getDBConnection()->find($instance);
	 */
	public function find($instance)
	{
		$database = $instance->getDB();
		try
		{
			$statement;

			$statement = $statement.$this->setMapValueToStatement($database);

			$this->setSql("SELECT * FROM `".$database['tableName']."` WHERE 1=1 ".$statement);
			$this->query();


			$result = $this->setToObject($database);
			$this->free();

			if($this->getResultRows()>0)
			{
				return $result;
			}
			else
			{
				return NULL;
			}
		}
		catch (DatabaseException $e)
		{
			$e->execute();
		}

		return NULL;
	}

	/**
	 * @method ค้นหาและดึงค่าข้อมูลจากฐานข้อมูลโดยจะดึงหลายค่า
	 * @param instance = Bean ที่ต้องการนำไปเป็นเงื่อนไขในการค้นหา
	 * @return Bean ข้อมูลที่ได้จากการค้นหา (Array)
	 * @example $this->getDBConnection()->findAll($instance);
	 */
	public function findAll($instance)
	{
		$database = $instance->getDB();
		$list = array();
		try
		{
			$statement;
				
			$statement = $statement.$this->setMapValueToStatement($database);

			$this->setSql("SELECT * FROM `".$database['tableName']."` WHERE 1=1 ".$statement);

			$this->query();



			if($this->getResult() != NULL)
			{
				$i=0;

				while($i<$this->getResultRows())
				{
					$instance = $database['modelName']."Manager";
					$instance = new $instance();
					$instanceArray = array();

					foreach ($database['columns'] as $key => $column)
					{
						//echo mysql_result($this->getResult(),$i,$database['tableName'].".".$column['name']);
						$data = mysql_result($this->getResult(),$i,$database['tableName'].".".$column['name']);
						settype($data,$column['constraint']['type']);
						$instance->set($key,$data);

					}
					$instance->setVersion(mysql_result($this->getResult(),$i,$instanceDB['tableName'].".".self::$versionText));
					$instance->setState("update");
					$instanceArray = $instance;
					array_push($list, $instanceArray);
					$i++;
				}
				$this->free();
			}

			if($this->getResultRows()>0)
			{
				return $list;
			}
			else
			{
				return NULL;
			}


		}
		catch (DatabaseException $e)
		{
			$e->execute();
		}

		return NULL;
	}

	/**
	 * @method ค้นหาและดึงค่าข้อมูลจากฐานข้อมูลโดยจะดึงข้อมูลเพียงค่าเดียว (กรณีอยากระบุ SQL เอง)
	 * @param database = ชื่อชนิด Manager ที่ต้องการได้รับกลับมา, sql = คำสั่ง sql command ที่จะใช้ในการค้นหา
	 * @return Bean ข้อมูลที่ได้จากการค้นหา
	 * @example $this->getDBConnection()->findNative("File,User", "SELECT * FROM file,user WHERE file_upload_by=user_id");
	 */
	public function findNative($database,$sql)
	{
		$this->setSql($sql);
		$this->query();
		//$this->fetch();

		$resultList = array();
		$dbArray = split(",", $database);

		foreach ($dbArray as $db)
		{
			if(file_exists(MANAGERS_PATH.'/'.$db.'.manager.php'))
			{
				require_once MANAGERS_PATH.'/'.$db.".manager.php";
				$instance = $db."Manager";
				$instance = new $instance();
				$instanceDB = $instance->getDB();
				$instance = $this->setToObject($instanceDB);
				$resultList[$instanceDB['modelName']] = $instance;
			}
			else if(strpos($db,"DB[")==0)
			{
				require_once BEANS_SYSTEM_PATH.'/custom.bean.php';
				$customBean = new CustomBean();
				$customVar = split("\+",substr($db, 7, strlen($db)-8));

				foreach ($customVar as $var)
				{
					$customBean->set($var,mysql_result($this->getResult(),0,$var));
				}
				$resultList["Custom"] = $customBean;
			}
		}

		$this->free();


		if($this->getResultRows()>0)
		{
			return $resultList;
		}
		else
		{
			return NULL;
		}
	}

	/**
	 * @method ค้นหาและดึงค่าข้อมูลจากฐานข้อมูลโดยจะดึงข้อมูลหลายค่า (กรณีอยากระบุ SQL เอง)
	 * @param database = ชื่อชนิด Manager ที่ต้องการได้รับกลับมา, sql = คำสั่ง sql command ที่จะใช้ในการค้นหา
	 * @return Bean ข้อมูลที่ได้จากการค้นหา (Array)
	 * @example $this->getDBConnection()->findAllNative("File,User", "SELECT * FROM file,user WHERE file_upload_by=user_id");
	 */
	public function findAllNative($database,$sql)
	{
		$this->setSql($sql);
		$this->query();
		$list = array();
		$dbArray = split(",", $database);

		if($this->getResult() != NULL)
		{
			$i=0;

			while($i<$this->getResultRows())
			{
				$resultList = array();

				foreach ($dbArray as $db)
				{
					if(file_exists(MANAGERS_PATH.'/'.$db.'.manager.php'))
					{
						require_once MANAGERS_PATH.'/'.$db.".manager.php";
						$instance = $db."Manager";
						$instance = new $instance();
						$instanceDB = $instance->getDB();

						$instance = $instanceDB['modelName']."Manager";
						$instance = new $instance();
						$instanceDB = $instance->getDB();

						foreach ($instanceDB['columns'] as $key => $column)
						{
							$data = mysql_result($this->getResult(),$i,$instanceDB['tableName'].".".$column['name']);
							settype($data,$column['constraint']['type']);
							$instance->set($key,$data);
						}
						$instance->setVersion(mysql_result($this->getResult(),$i,$instanceDB['tableName'].".".self::$versionText));
						$instance->setState("update");
						$resultList[$instanceDB['modelName']] = $instance;
					}
					else if(strpos($db,"DB[")==0)
					{
						require_once BEANS_SYSTEM_PATH.'/custom.bean.php';
						$customBean = new CustomBean();
						$customVar = split("\+",substr($db, 7, strlen($db)-8));

						foreach ($customVar as $var)
						{
							$customBean->set($var,mysql_result($this->getResult(),$i,$var));
						}
						$resultList["Custom"] = $customBean;
					}
				}

				array_push($list, $resultList);
				$i++;
			}
			$this->free();
		}

		if($this->getResultRows()>0)
		{
			return $list;
		}
		else
		{
			return NULL;
		}
	}

	private function update($instance,$versionIgnore = FALSE)
	{
		$database = $instance->getDB();
		Console::write("update operation");

		try
		{
			$statement = array("","");

			foreach($database['columns'] as $data)
			{
				if($data['constraint']['primary'] == true)
				{
					self::validate($data);
					if($statement[0] != ""){
						$statement[0].=" AND ";
					}

					$statement[0] .= "`".$data['name']."` = ";

					if($data['constraint']['type']!="integer" && $data['constraint']['type']!="float" && $data['constraint']['type']!="double")
						$statement[0] .= "'".$data['value']."'";
					else
						$statement[0] .= $data['value'];
				}

				if($data['constraint']['primary'] == false)
				{
					self::validate($data);
					if($data['value']!=NULL && $data['value']!="")
					{
						if($statement[1] != ""){
							$statement[1].=", ";
						}
							
						$statement[1] .= "`".$data['name']."` = ";

						if($data['constraint']['type']!="integer" && $data['constraint']['type']!="float" && $data['constraint']['type']!="double")
							$statement[1] .= "'".$data['value']."'";
						else
							$statement[1] .= $data['value'];
					}
				}
			}

			if($versionIgnore==FALSE)
			{
				$this->setSql("SELECT * FROM `".$database['tableName']."` WHERE ".$statement[0]);
				$this->query();

				if($instance->getVersion() != mysql_result($this->getResult(),0,$database['tableName'].".".self::$versionText))
				{
					throw new DatabaseException(MSG_ERR_DATABASE_CANNOT_SAVE."<br/><br/><b>".MSG_SYSTEM_MESSAGE."</b><br/>:- เวอร์ชั่นของข้อมูลที่ต้องการแก้ไขไม่ตรงกัน โปรแกรมอาจถูกแก้ไขโดยผู้ใช้อื่น<br/><br/><b>".MSG_DATABASE_QUERY_STRING."</b><br/>:- ".$this->sql."<br/>");
				}
			}

			$this->setSql("UPDATE `".$database['tableName']."` SET ".$statement[1].", `".$database['tableName']."`.`".self::$versionText."` = `".$database['tableName']."`.`version`+1  WHERE ".$statement[0]);
			$this->query();
			if($this->getResultRows()<=0 && mysql_error(self::$connection))
			{
				throw new DatabaseException(MSG_ERR_DATABASE_CANNOT_SAVE."<br/><br/><b>".MSG_SYSTEM_MESSAGE."</b><br/>:- ".mysql_error(self::$connection)."<br/><br/><b>".MSG_DATABASE_QUERY_STRING."</b><br/>:- ".$this->sql."<br/>");
			}
			else
			{

				return $instance;
			}
		}
		catch (DatabaseException $e)
		{
			$e->execute();
		}
	}

	private function validate($data)
	{
		try
		{
			if($data['value'] != "")
			{
				switch($data['constraint']['type'])
				{
					case 'integer':
					case 'int':
						if(is_int($data['value']) == false &&  is_integer($data['value']) == false)
						{
							throw new DatabaseException(MSG_ERR_DATABASE_CANNOT_SAVE."<br/><br/><b>".MSG_SYSTEM_MESSAGE."</b><br/>:- ข้อมูล ".$data['name']."(".$data['value'].") ต้องเป็นข้อมูลชนิด ".$data['constraint']['type']." เท่านั้น<br/>");
						}
						break;

					case 'string':
					case 'varchar':
					case 'char':
					case 'longtext':
						if(is_string($data['value']) == false)
						{
							$error = true;
							throw new DatabaseException(MSG_ERR_DATABASE_CANNOT_SAVE."<br/><br/><b>".MSG_SYSTEM_MESSAGE."</b><br/>:- ข้อมูล ".$data['name']."(".$data['value'].") ต้องเป็นข้อมูลชนิด ".$data['constraint']['type']." เท่านั้น<br/>");
						}
						break;

					case 'double':
					case 'long':
					case 'float':
						if(is_float($data['value']) == false && is_long($data['value']) == false && is_double($data['value']) == false)
						{
							throw new DatabaseException(MSG_ERR_DATABASE_CANNOT_SAVE."<br/><br/><b>".MSG_SYSTEM_MESSAGE."</b><br/>:- ข้อมูล ".$data['name']."(".$data['value'].") ต้องเป็นข้อมูลชนิด ".$data['constraint']['type']." เท่านั้น<br/>");
						}
						break;

					case 'date':
						if(is_date($data['value']) == false)
						{
							throw new DatabaseException(MSG_ERR_DATABASE_CANNOT_SAVE."<br/><br/><b>".MSG_SYSTEM_MESSAGE."</b><br/>:- ข้อมูล ".$data['name']."(".$data['value'].") ต้องเป็นข้อมูลชนิด ".$data['constraint']['type']." เท่านั้น<br/>");
						}
						break;
				}

				if($data['constraint']['length']!=NULL && strlen($data['value']) > $data['constraint']['length'] && $data['constraint']['length']!=0)
				{
					throw new DatabaseException(MSG_ERR_DATABASE_CANNOT_SAVE."<br/><br/><b>".MSG_SYSTEM_MESSAGE."</b><br/>:- ข้อมูล ".$data['name']."(".$data['value'].") ต้องเป็นข้อมูลที่มีความยาวไม่เกิน ".$data['constraint']['length']." เท่านั้น<br/>");
				}
			}
			else if($data['constraint']['nullable'] == false && $data['value'] == "")
			{
				throw new DatabaseException(MSG_ERR_DATABASE_CANNOT_SAVE."<br/><br/><b>".MSG_SYSTEM_MESSAGE."</b><br/>:- ข้อมูล ".$data['name']." ห้ามเป็นค่าว่าง<br/>");
			}
		}
		catch (DatabaseException $e)
		{
			$e->execute();
		}
	}

	public static function XMLSQL($class,$selector)
	{
		$xml = simplexml_load_file(SQLCOMMANDS_PATH."/".$class.".sql.xml");

		$result = $xml->xpath("/sqlcommands/sql[@name='".$selector."']");

		if($result[0]!="")
		{
			return $result[0];
		}

		return "";
	}

	private function free()
	{
		if($this->getResult()!= NULL)
		{
			@mysql_free_result($this->getResult());
			//Console::write("MYSQL FREE RESULT");
		}
	}

	/**
	 * @method ปิด Connection ที่ใช้ติดต่อกับฐานข้อมูล
	 * @param ไม่มี
	 * @return ไม่มี
	 * @example DB::disconnect();
	 */
	public static function disconnect()
	{
		if (self::$connection)
			mysql_close(self::$connection);
	}
}
?>